package com.luoyx.vjsb.authority.security.handler;

import cn.hutool.core.util.StrUtil;
import com.luoyx.vjsb.authority.security.exception.LoginFailLimitException;
import com.luoyx.vjsb.authority.security.properties.TokenProperties;
import com.luoyx.vjsb.common.util.AjaxResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 登录失败处理类
 * </p>
 *
 * @author luoyuanxiang
 * @date 2020/5/4 14:17
 */
@Slf4j
@Component
public class AuthenticationFailHandler extends SimpleUrlAuthenticationFailureHandler {

    @Resource
    private TokenProperties tokenProperties;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        if (exception instanceof UsernameNotFoundException || exception instanceof BadCredentialsException) {
            String userName = request.getParameter("username");
            recordLoginTime(userName);
            String key = "loginTimeLimit:" + userName;
            String value = stringRedisTemplate.opsForValue().get(key);
            if (StrUtil.isBlank(value)) {
                value = "0";
            }
            //获取已登录错误次数
            int loginFailTime = Integer.parseInt(value);
            int restLoginTime = tokenProperties.getLoginTimeLimit() - loginFailTime;
            log.info("用户" + userName + "登录失败，还有" + restLoginTime + "次机会");
            if (restLoginTime <= 3 && restLoginTime > 0) {
                AjaxResult.out(response, AjaxResult.error("用户名或密码错误，还有" + restLoginTime + "次尝试机会"));
            } else if (restLoginTime <= 0) {
                AjaxResult.out(response, AjaxResult.error("登录错误次数超过限制，请" + tokenProperties.getLoginAfterTime() + "分钟后再试"));
            } else {
                AjaxResult.out(response, AjaxResult.error("用户名或密码错误"));
            }
        } else if (exception instanceof DisabledException || exception instanceof LockedException) {
            AjaxResult.out(response, AjaxResult.error("账户被禁用，请联系管理员"));
        } else if (exception instanceof LoginFailLimitException) {
            AjaxResult.out(response, AjaxResult.error(exception.getMessage()));
        } else {
            AjaxResult.out(response, AjaxResult.error("登录失败，其他内部错误"));
        }
    }

    /**
     * 判断用户登陆错误次数
     *
     * @param userName 用户名称
     */
    public void recordLoginTime(String userName) {

        String key = "loginTimeLimit:" + userName;
        String flagKey = "loginFailFlag:" + userName;
        String value = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isBlank(value)) {
            value = "0";
        }
        // 获取已登录错误次数
        Integer loginFailTime = Integer.parseInt(value) + 1;
        stringRedisTemplate.opsForValue().set(key, String.valueOf(loginFailTime), tokenProperties.getLoginAfterTime(), TimeUnit.MINUTES);
        if (loginFailTime >= tokenProperties.getLoginTimeLimit()) {
            stringRedisTemplate.opsForValue().set(flagKey, "fail", tokenProperties.getLoginAfterTime(), TimeUnit.MINUTES);
        }
    }
}
